package com.itheima.grow.util;

import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.metadata.data.DataFormatData;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.springframework.http.HttpHeaders;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Set;

/**
 * easyExcel 导入导出工具类
 *
 * @author tingsheng.xie
 * @since 2023/11/21 17:24
 */
@Slf4j
public class ExcelUtil {



    private ExcelUtil() {

    }
    /**
     * 写出一个 excel 文件到本地
     * <br />
     * 将类型所有加了 @ExcelProperty 注解的属性全部写出
     *
     * @param fileName  文件名 不要后缀
     * @param sheetName sheet名
     * @param data      写出的数据
     * @param clazz     要写出数据类的Class类型对象
     * @param <T>       写出的数据类型
     */
    public static <T> void writeExcel(String fileName, String sheetName, List<T> data, Class<T> clazz) {
        writeExcel(null, fileName, sheetName, data, clazz);
    }


    /**
     * 按照指定的属性名进行写出 一个 excel
     *
     * @param attrName  指定的属性名 必须与数据类型的属性名一致
     * @param fileName  文件名 不要后缀
     * @param sheetName sheet名
     * @param data      要写出的数据
     * @param clazz     要写出数据类的Class类型对象
     * @param <T>       要写出的数据类型
     */
    public static <T> void writeExcel(Set<String> attrName, String fileName, String sheetName, List<T> data, Class<T> clazz) {
        fileName = StrUtil.isBlank(fileName) ? "fileName" : fileName;
        sheetName = StrUtil.isBlank(sheetName) ? "sheet0" : sheetName;

        try(FileOutputStream fos = new FileOutputStream(fileName)) {
            write(fos,attrName,sheetName,data,clazz);
        } catch (Exception exception) {
         log.error(exception.getMessage());
        }


    }

    /**
     * 导出  一个 excel
     *         导出excel所有数据
     * @param response
     * @param fileName  件名 最好为英文，不要后缀名
     * @param sheetName sheet名
     * @param data      要写出的数据
     * @param clazz     要写出数据类的Class类型对象
     * @param <T>       要写出的数据类型
     */
    public static <T> void export(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> clazz) {
        export(response, null, fileName, sheetName, data, clazz);
    }

    /**
     * 按照指定的属性名进行写出 一个 excel
     *
     * @param response
     * @param attrName  指定的属性名 必须与数据类型的属性名一致
     * @param fileName  文件名 最好为英文，不要后缀名
     * @param sheetName sheet名
     * @param data      要写出的数据
     * @param clazz     要写出数据类的Class类型对象
     * @param <T>       要写出的数据类型
     */
    public static <T> void export(HttpServletResponse response, Set<String> attrName, String fileName, String sheetName, List<T> data, Class<T> clazz) {
        sheetName = StrUtil.isBlank(sheetName) ? "sheet0" : sheetName;
        fileName = StrUtil.isBlank(fileName) ? "fileName" : fileName;
        fileName=fileName+getCurrentTime();
        // 这里URLEncoder.encode可以防止中文乱码
        String encodedFileName = null;
        try {
            encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replaceAll("\\+", "%20");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + encodedFileName + ExcelTypeEnum.XLSX.getValue());
        try(OutputStream os = response.getOutputStream()) {
            write(os,attrName,sheetName,data,clazz);
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }


    private static <T> void write(OutputStream os, Set<String> attrName, String sheetName, List<T> data, Class<T> clazz) {
        ExcelWriterBuilder write = EasyExcel.write(os, clazz);


        // 如果没有指定要写出那些属性数据，则写出全部
        if (!CollectionUtils.isEmpty(attrName)) {
            write.includeColumnFiledNames(attrName);
        }
        write
            // 自适应宽度，但是这个不是特别精确
            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
            .registerWriteHandler(getStyleStrategy())
                // .registerWriteHandler(new RowFormatSetTextHandler(clazz.))
//            .registerWriteHandler(new CustomCellWriteUtil())
               // .registerWriteHandler(new ExcelCellWriteHandler())
            .sheet(sheetName)
            .doWrite(data);
    }


    /**
     * 设置excel样式
     * 返回样式 Style 具体细节可以在网上查找样式元素 自行替换 现在这个也是可以直接套用的
     * @return
     */
    public static HorizontalCellStyleStrategy getStyleStrategy() {
        // 头的策略  样式调整
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 头背景 灰色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        WriteFont headWriteFont = new WriteFont();
        // 头字号
        headWriteFont.setFontHeightInPoints((short) 12);
        // 字体样式
//        headWriteFont.setFontName("宋体")
        headWriteCellStyle.setWriteFont(headWriteFont);
        // 自动换行
        headWriteCellStyle.setWrapped(false);
        // 设置细边框
        headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        headWriteCellStyle.setBorderRight(BorderStyle.THIN);
        headWriteCellStyle.setBorderTop(BorderStyle.THIN);
        // 设置边框颜色 25灰度
        headWriteCellStyle.setBottomBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        headWriteCellStyle.setTopBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        headWriteCellStyle.setLeftBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        headWriteCellStyle.setRightBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        // 水平对齐方式
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 垂直对齐方式
        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 内容的策略 宋体
        WriteCellStyle contentStyle = new WriteCellStyle();
        // 设置垂直居中
        contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 设置 水平居中
        contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        WriteFont contentWriteFont = new WriteFont();
        // 内容字号
        contentWriteFont.setFontHeightInPoints((short) 12);
        DataFormatData dataFormatData = new DataFormatData();
        dataFormatData.setIndex((short) 12);
        contentStyle.setDataFormatData(dataFormatData);
        // 字体样式
//        contentWriteFont.setFontName("宋体")
        contentStyle.setWriteFont(contentWriteFont);
        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentStyle);
    }

    public static void resetCellMaxTextLength() {
        SpreadsheetVersion excel2007 = SpreadsheetVersion.EXCEL2007;
        if (Integer.MAX_VALUE != excel2007.getMaxTextLength()) {
            Field field;
            try {
                field = excel2007.getClass().getDeclaredField("_maxTextLength");
                field.setAccessible(true);
                field.set(excel2007,Integer.MAX_VALUE);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static String getCurrentTime(){
        long l = System.currentTimeMillis(); //获取时间戳效率最高
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String format = dateFormat.format(l);
        return format;
    }



}
